package api

import (
	"context"
	"fmt"
	"go_final/mxshop_api/user_web/forms"
	"go_final/mxshop_api/user_web/global"
	"go_final/mxshop_api/user_web/global/response"
	middlewares "go_final/mxshop_api/user_web/middleware"
	"go_final/mxshop_api/user_web/models"
	userpb "go_final/mxshop_api/user_web/proto/protofile"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/go-redis/redis/v8"

	jwt2 "github.com/dgrijalva/jwt-go"

	"github.com/go-playground/validator/v10"

	"google.golang.org/grpc/codes"

	"google.golang.org/grpc/status"

	"github.com/gin-gonic/gin"
	"go.uber.org/zap"
)

func removeTopStruct(fields map[string]string) map[string]string {
	res := map[string]string{}
	for field, err := range fields {
		res[field[strings.Index(field, ".")+1:]] = err
	}
	return res
}

// HandleGrpcError2Http 将grpc错误转换成http错误返回
func HandleGrpcError2Http(err error, c *gin.Context) {
	if e, ok := status.FromError(err); ok {
		switch e.Code() {
		case codes.NotFound:
			c.JSON(http.StatusNotFound, gin.H{
				"msg": e.Message(),
			})
		case codes.Internal:
			c.JSON(http.StatusInternalServerError, gin.H{
				"msg": "内部错误",
			})
		case codes.InvalidArgument:
			c.JSON(http.StatusBadRequest, gin.H{
				"msg": "参数错误",
			})
		case codes.Unavailable:
			c.JSON(http.StatusInternalServerError, gin.H{
				"msg": "用户服务不可用",
			})
		default:
			c.JSON(http.StatusInternalServerError, gin.H{
				"msg": "其他错误:" + e.Message(),
			})
		}
	}
}

func HandleValidateError(c *gin.Context, err error) {
	// 如何返回错误信息
	errs, ok := err.(validator.ValidationErrors)
	if !ok {
		c.JSON(http.StatusOK, gin.H{
			"msg": err.Error(),
		})
		return
	}

	c.JSON(http.StatusBadRequest, gin.H{
		"error": removeTopStruct(errs.Translate(global.Trans)),
	})
}

// GetUserList 获取用户列表
func GetUserList(c *gin.Context) {
	claims, _ := c.Get("claims")
	zap.S().Infof("访问用户: %d", claims.(*models.CustomClaims).ID)

	pageNum := c.DefaultQuery("pageNum", "1")
	pageNumInt, _ := strconv.Atoi(pageNum)
	pageSize := c.DefaultQuery("pageSize", "10")
	pageSizeInt, _ := strconv.Atoi(pageSize)

	res, err := global.UserClient.GetUserList(context.Background(), &userpb.PageInfo{
		PageNum:  uint32(pageNumInt),
		PageSize: uint32(pageSizeInt),
	})

	if err != nil {
		zap.S().Errorw("invoke grpc GetUserList err", "msg", err.Error())
		HandleGrpcError2Http(err, c)
		return
	}

	result := make([]response.UserResponse, 0)
	for _, value := range res.Data {
		user := response.UserResponse{
			ID:       value.ID,
			Mobile:   value.Mobile,
			NickName: value.NickName,
			Birthday: response.JsonTime(time.Unix(int64(value.Birthday), 0)),
			Gender:   value.Gender,
		}
		result = append(result, user)
	}

	c.JSON(http.StatusOK, result)
}

// PasswordLogin 账号密码登录
func PasswordLogin(c *gin.Context) {
	// 表单验证
	passwordLoginForm := forms.PasswordLoginForm{}
	if err := c.ShouldBind(&passwordLoginForm); err != nil {
		HandleValidateError(c, err)
		return
	}

	// 校验验证码
	if !store.Verify(passwordLoginForm.CaptchaID, passwordLoginForm.Captcha, true) {
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": "验证码错误",
		})
		return
	}

	userInfo, err := global.UserClient.GetUserByMobile(context.Background(), &userpb.GetUserByMobileRequest{
		Mobile: passwordLoginForm.Mobile,
	})

	if err != nil {
		if e, ok := status.FromError(err); ok {
			switch e.Code() {
			case codes.NotFound:
				c.JSON(http.StatusNotFound, gin.H{
					"msg": "用户不存在",
				})
			default:
				c.JSON(http.StatusInternalServerError, gin.H{
					"msg": "登录失败",
				})
			}
			return
		}
	} else {
		// 查询到了用户信息, 检查用户密码
		checkRes, err := global.UserClient.CheckUserPassword(context.Background(), &userpb.CheckUserPasswordRequest{
			Password:          passwordLoginForm.Password,
			EncryptedPassword: userInfo.Password,
		})
		if err != nil {
			c.JSON(http.StatusInternalServerError, gin.H{
				"msg": "登录失败",
			})
			return
		} else {
			if checkRes.Checked {
				jwt := middlewares.NewJWT()
				claims := models.CustomClaims{
					ID:          uint(userInfo.ID),
					NickName:    userInfo.NickName,
					AuthorityId: uint(userInfo.Role),
					StandardClaims: jwt2.StandardClaims{
						NotBefore: time.Now().Unix(),              // 签名的生效时间
						ExpiresAt: time.Now().Unix() + 60*60*24*7, // 签名过期时间
						Issuer:    "Felix_Mxshop",
					},
				}
				token, err := jwt.CreateToken(claims)
				if err != nil {
					c.JSON(http.StatusInternalServerError, gin.H{
						"msg": "生成token失败",
					})
					return
				}

				c.JSON(http.StatusOK, gin.H{
					"id":         userInfo.ID,
					"nick_name":  userInfo.NickName,
					"token":      token,
					"expired_at": (time.Now().Unix() + 60*60*24*7) * 1000,
				})
			} else {
				c.JSON(http.StatusBadRequest, gin.H{
					"msg": "密码错误",
				})
			}
		}
	}
}

func Register(c *gin.Context) {
	// 表单验证
	registerForm := forms.RegisterForm{}
	if err := c.ShouldBind(&registerForm); err != nil {
		HandleValidateError(c, err)
		return
	}

	// 校验验证码
	// 保存验证码到redis中
	rdb := redis.NewClient(&redis.Options{
		Addr:     fmt.Sprintf("%s:%d", global.ServerConfig.RedisInfo.Host, global.ServerConfig.RedisInfo.Port),
		Password: "123456", // no password set
	})

	value, err := rdb.Get(context.Background(), registerForm.Mobile).Result()
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": "获取验证码失败",
		})
		return
	}

	if err == redis.Nil {
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": "验证不存在或者已失效",
		})
		return
	}

	// 获取验证码成功
	if value != registerForm.SMSCode {
		c.JSON(http.StatusBadRequest, gin.H{
			"msg": "验证码错误",
		})
		return
	}

	// 新建用户
	userInfo, err := global.UserClient.CreateUser(context.Background(), &userpb.CreateUserRequest{
		NickName: registerForm.Mobile,
		Password: registerForm.Password,
		Mobile:   registerForm.Mobile,
	})

	if err != nil {
		zap.S().Error("[Register]【新建用户失败】", err.Error())
		HandleGrpcError2Http(err, c)
		return
	}

	// 自动登录
	jwt := middlewares.NewJWT()
	claims := models.CustomClaims{
		ID:          uint(userInfo.ID),
		NickName:    userInfo.NickName,
		AuthorityId: uint(userInfo.Role),
		StandardClaims: jwt2.StandardClaims{
			NotBefore: time.Now().Unix(),              // 签名的生效时间
			ExpiresAt: time.Now().Unix() + 60*60*24*7, // 签名过期时间
			Issuer:    "Felix_Mxshop",
		},
	}
	token, err := jwt.CreateToken(claims)
	if err != nil {
		c.JSON(http.StatusInternalServerError, gin.H{
			"msg": "生成token失败",
		})
		return
	}

	c.JSON(http.StatusOK, gin.H{
		"id":         userInfo.ID,
		"nick_name":  userInfo.NickName,
		"token":      token,
		"expired_at": (time.Now().Unix() + 60*60*24*7) * 1000,
	})
}
